import { stringify } from 'yaml'

import {
  AdvancedConfigDefaults,
  DnsConfigDefaults,
  GeneralConfigDefaults,
  TunConfigDefaults,
  MixinConfigDefaults,
  ScriptConfigDefaults,
  BuiltInOutbound,
} from '@/constant'
import { RulesetBehavior, RulesetFormat, RuleType } from '@/enums/kernel'
import { deepAssign, sampleID } from '@/utils'

import type { ProfileType } from '@/stores'

export const restoreProfile = (
  config: Record<string, any>,
  subID: string,
  NameIdMap: Record<string, string>,
  IdNameMap: Record<string, string>,
) => {
  const profile: ProfileType = {
    // The same ID indicates that this profile was generated by this subscription
    id: subID,
    name: subID,
    generalConfig: GeneralConfigDefaults(),
    advancedConfig: AdvancedConfigDefaults(),
    dnsConfig: Object.assign(DnsConfigDefaults(), {
      'proxy-server-nameserver': [],
      'nameserver-policy': {},
    }),
    tunConfig: TunConfigDefaults(),
    proxyGroupsConfig: [],
    rulesConfig: [
      {
        id: RuleType.InsertionPoint,
        type: RuleType.InsertionPoint,
        enable: true,
        payload: '',
        proxy: '',
        'no-resolve': false,
        'ruleset-name': '',
        'ruleset-type': 'file',
        'ruleset-behavior': RulesetBehavior.Domain,
        'ruleset-format': RulesetFormat.Mrs,
        'ruleset-proxy': '',
        'ruleset-interval': 0,
      },
    ],
    mixinConfig: MixinConfigDefaults(),
    scriptConfig: ScriptConfigDefaults(),
  }

  const GroupNameIdMap: Record<string, string> = {}
  const GroupIdNameMap: Record<string, string> = {}

  config['proxy-groups'] = config['proxy-groups'] || []
  config['rule-providers'] = config['rule-providers'] || {}

  config['proxy-groups'].forEach((group: any) => {
    const id = sampleID()
    GroupNameIdMap[group.name] = id
    GroupIdNameMap[id] = group.name
  })

  function isBuiltIn(proxy: string) {
    return BuiltInOutbound.includes(proxy)
  }

  config['proxy-groups'].forEach((group: any) => {
    const _group: ProfileType['proxyGroupsConfig'][number] = {
      id: GroupNameIdMap[group.name]!,
      name: group.name,
      type: group.type,
      proxies: (group.proxies || [])
        .map((proxy: string) => ({
          id: GroupNameIdMap[proxy] || NameIdMap[proxy] || proxy,
          type: GroupNameIdMap[proxy] || isBuiltIn(proxy) ? 'Built-In' : subID,
          name: GroupNameIdMap[proxy]
            ? GroupIdNameMap[GroupNameIdMap[proxy]]
            : IdNameMap[NameIdMap[proxy]!] || (isBuiltIn(proxy) && proxy),
        }))
        // The absence of a 'name' attribute indicates that this proxy has been excluded after processing by filters or plugins.
        .filter((v: any) => v.name),
      url: group.url ?? 'https://www.gstatic.com/generate_204',
      interval: group.interval ?? 300,
      strategy: group.strategy ?? 'consistent-hashing',
      use: (group.use || []).map((use: string) => {
        return GroupNameIdMap[use] || use
      }),
      tolerance: group.tolerance ?? 150,
      lazy: group.lazy ?? true,
      'disable-udp': group['disable-udp'] ?? false,
      filter: group.filter ?? '',
      'exclude-filter': group['exclude-filter'] ?? '',
      hidden: group.hidden ?? false,
      icon: group.icon ?? '',
    }

    profile.proxyGroupsConfig.push(_group)
  })

  function getRuleProxy(type: string) {
    return BuiltInOutbound.includes(type.toUpperCase())
      ? type.toUpperCase()
      : GroupNameIdMap[type] || IdNameMap[NameIdMap[type]!]
  }

  let isGeoModeEnabled = false

  Object.entries(config).forEach(([field, value]) => {
    if (Object.hasOwnProperty.call(profile.generalConfig, field)) {
      ;(profile.generalConfig as any)[field] = value
    } else if (Object.hasOwnProperty.call(profile.advancedConfig, field)) {
      ;(profile.advancedConfig as any)[field] = value
    } else if (field === 'dns') {
      profile.dnsConfig = deepAssign(profile.dnsConfig, value)
    } else if (field === 'tun') {
      profile.tunConfig = deepAssign(profile.tunConfig, value)
    } else if (field === 'hosts') {
      Object.entries<string | string[]>(value).forEach(([key, value]) => {
        if (Array.isArray(value)) {
          profile.dnsConfig.hosts[key] = value.join(',')
        } else {
          profile.dnsConfig.hosts[key] = value
        }
      })
    } else if (field === 'rules') {
      config[field].forEach((rule: string, index: number) => {
        const [type = '', payload = '', proxy = '', noResolve] = rule.split(',')

        const _proxy = type === RuleType.Match ? getRuleProxy(payload) : getRuleProxy(proxy)

        // Skip invalid rules：proxy missing
        if (!_proxy) {
          return
        }

        if (type === RuleType.RuleSet) {
          const provider = config['rule-providers'][payload]
          // Skip invalid rules：rule-provider missing
          if (!provider) {
            return
          }
          profile.rulesConfig.push({
            id: index.toString(),
            type: type,
            enable: true,
            payload: provider.type == 'inline' ? stringify(provider.payload) : provider.url,
            proxy: _proxy,
            'no-resolve': !!noResolve,
            'ruleset-behavior': provider.behavior,
            'ruleset-format': provider.format || RulesetFormat.Yaml,
            'ruleset-type': provider.type || 'http',
            'ruleset-name': payload,
            'ruleset-proxy': 'DIRECT',
            'ruleset-interval': provider.interval ?? 0,
          })
          return
        }

        if (type === RuleType.Geoip || type === RuleType.Geosite) {
          isGeoModeEnabled = true
        }

        profile.rulesConfig.push({
          id: index.toString(),
          type: type as RuleType,
          enable: true,
          payload: type === 'MATCH' ? '' : payload,
          proxy: _proxy,
          'no-resolve': !!noResolve,
          'ruleset-behavior': RulesetBehavior.Domain,
          'ruleset-format': RulesetFormat.Mrs,
          'ruleset-type': 'http',
          'ruleset-name': '',
          'ruleset-proxy': '',
          'ruleset-interval': 0,
        })
      })
    }
  })

  profile.advancedConfig['geodata-mode'] = isGeoModeEnabled

  return profile
}
